﻿using FluentValidation;
using FluentValidation.Results;
using FluentValidation.Validators;
using RockBus.DataContracts;
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.XPath;

namespace RockBus.ConfigurationService.Validation
{
    public class SubscriptionInfoValidator : AbstractValidator<SubscriptionInfo>
    {
        public SubscriptionInfoValidator()
        {
            this.RuleFor(x => x.Detail).NotNull();
            this.RuleFor(x => x.Detail).SetValidator(new SubscriptionDetailValidator());
            this.RuleFor(x => x.FilterIds).NotNull().WithMessage("At least one FilterId must be specified.");
            this.RuleFor(x => x.FilterIds).NotEmpty().WithMessage("At least one FilterId must be specified.");
        }
    }

    public class SubscriptionDetailValidator : AbstractValidator<SubscriptionDetail>
    {
        public SubscriptionDetailValidator()
        {
            this.RuleFor(x => x.Id).NotNull();
            this.RuleFor(x => x.Id).NotEmpty();
            this.RuleFor(x => x.EndpointId).NotNull();
            this.RuleFor(x => x.EndpointId).NotEmpty();
        }
    }

    public class XPathValidator : PropertyValidator
    {
        public XPathValidator()
            : base("Property {PropertyName} contains invalid xpath expression!")
        {
        }

        private static readonly XmlDocument xdoc = new XmlDocument();
        private static readonly XPathNavigator xnav = xdoc.CreateNavigator();

        protected override bool IsValid(PropertyValidatorContext context)
        {
            string xpathString = (string)context.PropertyValue;
            if (string.IsNullOrEmpty(xpathString))
            {
                return true;
            }
            try
            {
                XPathExpression expr = xnav.Compile(xpathString);
            }
            catch (XPathException)
            {
                return false;
            }
            return true;
        }
    }

    public class RegexValidator : PropertyValidator
    {
        public RegexValidator()
            : base("Property {PropertyName} contains invalid regular expression!")
        {
        }

        protected override bool IsValid(PropertyValidatorContext context)
        {
            string regexString = (string)context.PropertyValue;
            if (string.IsNullOrEmpty(regexString))
            {
                return true;
            }

            try
            {
                Regex r = new Regex(regexString);
            }
            catch (ArgumentNullException)
            {
                return false;
            }
            return true;
        }
    }

    public class SubscriberInfoMustContainSubscriptionElementIdsValidator : PropertyValidator
    {
        public SubscriberInfoMustContainSubscriptionElementIdsValidator()
            : base("")
        {
        }

        private string _validationMessage;

        protected override bool IsValid(PropertyValidatorContext context)
        {
            var si = context.PropertyValue as SubscriptionInfo;
            if (si != null)
            {
                return false;
            }

            bool isValid = true;
            StringBuilder sb = new StringBuilder("Non-existing filterIds: ");
            foreach (var filterId in si.FilterIds)
            {
                isValid = false;
                if (!si.SubscriberInfo.FilterInfoDictionary.ContainsKey(filterId))
                {
                    sb.Append(filterId);
                    sb.Append(", ");
                }
            }

            isValid = false;
            if (!si.SubscriberInfo.FilterInfoDictionary.ContainsKey(si.Detail.EndpointId))
            {
                sb.Append("Non-existing endpoints: ");
                sb.Append(si.Detail.EndpointId);
            }

            this._validationMessage = sb.ToString();
            return isValid;
        }

        protected override ValidationFailure CreateValidationError(PropertyValidatorContext context)
        {
            var failure = new ValidationFailure(context.PropertyName, this._validationMessage, context.PropertyValue);

            if (CustomStateProvider != null)
            {
                failure.CustomState = CustomStateProvider(context.Instance);
            }

            return failure;
        }
    }
}